package webx.utils;

import java.util.*;
import webx.LogFile;
import dbx.RedisConnect;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;

public class RedisHelper {
	public static final int ERROR = -1;
	public static final int NOTFOUND = -2;
	private static ThreadLocal<Integer> status = new ThreadLocal<Integer>();

	public static int getStatus(){
		return status.get();
	}
    public static void release(Jedis redis){
        redis.close();
    }
	public static String getString(String ...args){
	    String key = "";
        for (String item : args) key += "," + item;
        return key.isEmpty() ? key : key.substring(1);
    }
    public static Jedis getconn() throws Exception{
        status.set(0);
        return RedisConnect.Connect();
    }
	private static void errorTrace(String msg, Exception error){
		status.set(ERROR);

		if (error == null){
            LogFile.Error(msg);
        }
        else{
            LogFile.Error(msg, "[" + error.getLocalizedMessage() + "]");
            error.printStackTrace();
        }
	}
	private static void noticeTrace(String msg){
	    LogFile.Trace(msg);
	}

	public static int getRandomTimeout(){
		return getRandomTimeout(600);
	}
	public static int getRandomTimeout(int timeout){
		int tmp = timeout / 2 + (int)(Math.random() * timeout) / 2;
		return tmp > 10 ? tmp : timeout;
	}

    public static String getLockTag(){
        return "user.progress.utils.thread." + Thread.currentThread().getId();
    }
    public static boolean lock(String key){
        return lock(key, 30);
    }
    public static boolean unlock(String key){
        Long flag = 1L;
        Jedis redis = null;
        boolean res = false;
        String pid = getLockTag();
        String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

        try{
            redis = getconn();
            Object result = redis.eval(lua, Collections.singletonList(key), Collections.singletonList(pid));

            if (result == null){
                errorTrace("redis unlock[" + key + "] failed", null);
                res = false;
            }
            else if (result.equals(flag)){
                noticeTrace("redis unlock[" + key + "] success");
                res = true;
            }
            else{
                noticeTrace("redis unlock[" + key + "] failed");
                res = false;
            }
        }
        catch(Exception e){
            errorTrace("redis unlock[" + key + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }
    public static boolean lock(String key, int timeout){
        Jedis redis = null;
        boolean res = false;
        int ms = timeout * 1000;
        String pid = getLockTag();

        try{
            redis = getconn();

            while (ms > 0) {
                String msg = redis.set(key, pid, "nx", "ex", timeout);
                if (msg == null){
                    Thread.sleep(100);
                    ms -= 100;
                }
                else {
                    noticeTrace("redis lock[" + key + "] success");
                    res = true;
                    break;
                }
            }

            if (ms <= 0) noticeTrace("redis lock[" + key + "] timeout");
        }
        catch(Exception e){
            errorTrace("redis lock[" + key + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }

	public static String get(String key){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.get(key);
			if (val == null) val = "";
			noticeTrace("redis get[" + key + "][" + (val.length() > 1024 ? val.substring(0, 1024) : val) + "] success");
		}
		catch(Exception e){
			errorTrace("redis get[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val == null ? "" : val;
	}
    public static long del(String ...args){
        long res = ERROR;
        Jedis redis = null;

        try{
            redis = getconn();
            res = redis.del(args);
            noticeTrace("redis del[" + getString(args) + "]");
        }
        catch(Exception e){
            errorTrace("redis del[" + getString(args) + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }
    public static Set<String> keys(String pattern){
        Jedis redis = null;
        Set<String> keyset = null;

        try{
            redis = getconn();
            keyset = redis.keys(pattern);
            noticeTrace("redis keys[" + pattern + "] success");
        }
        catch(Exception e){
            errorTrace("redis keys[" + pattern + "] failed", e);
        }
        finally{
            release(redis);
        }

        return keyset;
    }
	public static List<String> mget(String...args){
		Jedis redis = null;
		List<String> vec = null;

		try{
			redis = getconn();
			vec = redis.mget(args);
			noticeTrace("redis mget[" + getString(args) + "] success");
		}
		catch(Exception e){
			errorTrace("redis mget[" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return vec;
	}
    public static long expire(String key, int timeout){
        long res = ERROR;
        Jedis redis = null;

        try{
            redis = getconn();
            res = redis.expire(key, timeout);
            noticeTrace("redis expire[" + key + "][" + timeout + "] success");
        }
        catch(Exception e){
            errorTrace("redis expire[" + key + "][" + timeout + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }

	public static boolean mset(String...args){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			redis.mset(args);
			res = true;
			noticeTrace("redis mset[" + getString(args) + "] success");
		}
		catch(Exception e){
			errorTrace("redis mset[" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean set(String key, String val){
		return set(key, val, 0, true);
	}
	public static boolean set(String key, String val, int timeout){
		return set(key, val, timeout, true);
	}
	public static boolean set(String key, String val, int timeout, boolean updated){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			if (updated) {
				if (timeout > 0) redis.setex(key, timeout, val);
				else redis.set(key, val);
			}
			else{
				String msg = redis.set(key, val, "nx", "ex", timeout);
				if (msg == null) throw new Exception("redis execute failed");
			}
			noticeTrace("redis set[" + key + "][" + (val.length() > 1024 ? val.substring(0, 1024) : val) + "] success");
			res = true;
		}
		catch(Exception e){
			errorTrace("redis set[" + key + "][" + (val.length() > 1024 ? val.substring(0, 1024) : val) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static long scard(String key){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.scard(key);
			noticeTrace("redis scard[" + key + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis scard[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long sadd(String key, String...args){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.sadd(key, args);
			noticeTrace("redis sadd[" + key + "][" + getString(args) + "] success");
		}
		catch(Exception e){
			errorTrace("redis sadd[" + key + "][" + getString(args) + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean sismember(String key, String val){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			res = redis.sismember(key, val);
			noticeTrace("redis sismember[" + key + "][" + val + "][" + res + "] success");
		}
		catch(Exception e){
			errorTrace("redis sismember[" + key + "][" + val + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static long hdel(String key, String field){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.hdel(key, field);
			noticeTrace("redis hdel[" + key + "][" + field + "] success");
		}
		catch(Exception e){
			errorTrace("redis hdel[" + key + "][" + field + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static String hget(String key, String field){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.hget(key, field);
			if (val == null) val = "";
			noticeTrace("redis hget[" + key + "][" + field + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis hget[" + key + "][" + field + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val;
	}
	public static boolean hset(String key, String field, String val){
		Jedis redis = null;
		boolean res = false;

		try{
			redis = getconn();
			redis.hset(key, field, val);
			noticeTrace("redis hset[" + key + "][" + field + "][" + val + "] success");
			res = true;
		}
		catch(Exception e){
			errorTrace("redis hset[" + key + "][" + field + "][" + val + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}

    public static Tuple zget(String key, long pos){
        List<Tuple> res = zrange(key, pos, pos);
        if (res == null || res.isEmpty()) return null;
        for (Tuple item : res) return item;
        return null;
    }
    public static long zrank(String key, String member){
        long res = ERROR;
        Jedis redis = null;

        try{
            redis = getconn();
            res = redis.zrank(key, member);
            noticeTrace("redis zrank[" + key + "][" + member + "][" + res + "] success");
        }
        catch(Exception e){
            errorTrace("redis zrank[" + key + "][" + member + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }
    public static double zscore(String key, String member){
        double res = ERROR;
        Jedis redis = null;

        try{
            redis = getconn();
            Double tmp = redis.zscore(key, member);
            if (tmp == null){
                status.set(NOTFOUND);
                return res;
            }
            res = tmp;
            noticeTrace("redis zscore[" + key + "][" + member + "][" + res + "] success");
        }
        catch(Exception e){
            errorTrace("redis zscore[" + key + "][" + member + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }
    public static long zadd(String key, Map<String, Double> data){
        long res = ERROR;
        Jedis redis = null;

        try{
            redis = getconn();
            res = redis.zadd(key, data);
            noticeTrace("redis zadd[" + key + "][" + data.size() + "] success");
        }
        catch(Exception e){
            errorTrace("redis zadd[" + key + "][" + data.size() + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }
	public static long zadd(String key, double score, String member){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.zadd(key, score, member);
			noticeTrace("redis zadd[" + key + "][" + member + "][" + score + "] success");
		}
		catch(Exception e){
			errorTrace("redis zadd[" + key + "][" + member + "][" + score + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static List<Tuple> zrange(String key, long start, long end){
		Jedis redis = null;
		Set<Tuple> res = null;

		try{
			redis = getconn();
			res = redis.zrangeWithScores(key, start, end);
			noticeTrace("redis zrange[" + key + "][" + start + "][" + end + "] success");
		}
		catch(Exception e){
			errorTrace("redis zrange[" + key + "][" + start + "][" + end + "] failed", e);
		}
		finally{
			release(redis);
		}

		if (res == null) return null;

		ArrayList<Tuple> list = new ArrayList<Tuple>();

		for (Tuple item : res) list.add(item);

		Collections.sort(list, new Comparator<Tuple>() {
			@Override
			public int compare(Tuple a, Tuple b) {
				double m = a.getScore();
				double n = b.getScore();
				if (m == n) return 0;
				return m > n ? 1 : -1;
			}
		});

		return list;
	}
    public static double zincrby(String key, double score, String member){
        double res = ERROR;
        Jedis redis = null;

        try{
            redis = getconn();
            res = redis.zincrby(key, score, member);
            noticeTrace("redis zincrby[" + key + "][" + member + "][" + res + "] success");
        }
        catch(Exception e){
            errorTrace("redis zincrby[" + key + "][" + member + "] failed", e);
        }
        finally{
            release(redis);
        }

        return res;
    }

	public static String lpop(String key){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.lpop(key);
			noticeTrace("redis lpop[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis lpop[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val;
	}
	public static String rpop(String key){
		String val = null;
		Jedis redis = null;

		try{
			redis = getconn();
			val = redis.rpop(key);
			noticeTrace("redis rpop[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis rpop[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return val;
	}
	public static long lpush(String key, String val){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.lpush(key, val);
			noticeTrace("redis lpush[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis lpush[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long rpush(String key, String val){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.rpush(key, val);
			noticeTrace("redis rpush[" + key + "][" + val + "] success");
		}
		catch(Exception e){
			errorTrace("redis rpush[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static boolean trim(String key, long start, long end){
		Jedis redis = null;
		boolean res = true;

		try{
			redis = getconn();
			redis.ltrim(key, start, end);
			noticeTrace("redis ltrim[" + key + "][" + start + ":" + end + "] success");
		}
		catch(Exception e){
			errorTrace("redis ltrim[" + key + "][" + start + ":" + end + "] failed", e);
			res = false;
		}
		finally{
			release(redis);
		}

		return res;
	}

	public static long incr(String key){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.incr(key);
			noticeTrace("redis incr[" + key + "] success");
		}
		catch(Exception e){
			errorTrace("redis incr[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
	public static long decr(String key){
		long res = ERROR;
		Jedis redis = null;

		try{
			redis = getconn();
			res = redis.decr(key);
			noticeTrace("redis decr[" + key + "] success");
		}
		catch(Exception e){
			errorTrace("redis decr[" + key + "] failed", e);
		}
		finally{
			release(redis);
		}

		return res;
	}
}